﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.Storage;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using WinRTXamlToolkit.Controls.DataVisualization.Charting;

// https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x804 上介绍了“空白页”项模板


//namespace App2
//{

//};


class Report
{
    public string months { get; set; }
    public int countlent { get; set; }
}


namespace App2
{
    public class LineSeriesAxis : LinearAxis
    {
        protected override double CalculateActualInterval(Size availableSize)
        {
            var result = base.CalculateActualInterval(availableSize);
            return (result < 1.0) ? 1.0 : result;
        }
    }

    /// <summary>
    /// 可用于自身或导航至 Frame 内部的空白页。
    /// </summary>
    public sealed partial class MainPage : Page
    {
        private NutBle nutble;

        DispatcherTimer timer;
        private int base_y = 0;


        private List<byte[]> org_datas = new List<byte[]>();

        //private List<Report> buf_reports = new List<Report>();

        //private int seed_i = 0;
        //private PointCollection pcs = new PointCollection();
        //private List<Point> pcs_buf = new List<Point>();


        public MainPage()
        {
            this.InitializeComponent();
            DBG_GOBAL.log_textbox = log_textbox;
            nutble = new NutBle();

            nutble.mainPage = this;

            timer = new DispatcherTimer();
            timer.Interval = new TimeSpan(0, 0, 1);
            timer.Tick += timer_tick;
            timer.Start();

            //var l = new PointCollection();
            //l.Add(new Point(0, 0));
            //l.Add(new Point(130, 40));
            ////polyline.DataContext = l;
            //polyline.Points = l;
            //pcs.Add(new Point(0, 0));

            //polyline.
            //System.Threading.Timer timer = new System.Threading.Timer(
            //new System.Threading.TimerCallback(refresh), null, 20, 1000);

            //byte[] test_data = new byte[3];
            //test_data[0] = 0x7f;
            //test_data[1] = 0xff;
            //test_data[2] = 0xff;

            //var i = convert_wave_org_data(test_data, 0);
            ////DBG_GOBAL.WriteLine("conver val: " + i.ToString());
            //log_textbox.Text = "conver val: " + i.ToString();
        }

        private void wave_zoom(int zoom_x, int zoom_y, int p_x, int p_y)
        {
            //polyline.RenderTransform.TransformBounds(new Rect(new Point(0, 0), new Point(100, 100)));

            //RenderTransform = new CompositeTransform { TranslateY = 20 };
            var ctf = new CompositeTransform();
            if (zoom_x == 0) zoom_x = 1;
            if (zoom_y == 0) zoom_y = -1;
            ctf.ScaleX = zoom_x;
            ctf.ScaleY = zoom_y;
            ctf.TranslateX = p_x;
            ctf.TranslateY = p_y;

            //var o = DependencyProperty.Register("ScaleX", typeof(double), typeof(CompositeTransform), null);
            //polyline.RenderTransform.SetValue(o, 10);
            polyline.RenderTransform = ctf;

        }


        private long wave_max_base = 0x7FFFFF;
        private long wave_area_base_y = 10;
        private double wave_max_base_real = 33;
        private double y_mul = 1;
        private double x_mul = 1;

        private double calc_point(long ival)
        {
            double  val = y_mul * wave_area_base_y * wave_max_base_real * ival / wave_max_base;
            return val;
        }

        List<long> wave_data_buf = new List<long>();
        private int wave_data_buf_inx = 0;

        private int wave_data_pkg_len = 3;


        private int convert_wave_org_data(byte[] data, int inx)
        {
            byte[] c_data = new byte[4];
            try
            {
                c_data[3] = 0;
                c_data[2] = data[inx];
                c_data[1] = data[inx + 1];
                c_data[0] = data[inx + 2];
                var rs = BitConverter.ToInt32(c_data, 0);
                return rs;
            }
            catch
            {
                DBG_GOBAL.WriteLine("wave org data format error");
                return 0;
            }
        }

        private void convert_wave_data_list(List<byte[]> wave_datas, int count)
        {
            for(var j=0; j<count; j++)
            {
                var item_len = wave_datas[j].Length;
                //for align pkg byte
                if (item_len % wave_data_pkg_len != 0)
                {
                    item_len = item_len - item_len % wave_data_pkg_len;
                }
                for (var i=0; i<wave_datas[j].Length; i += wave_data_pkg_len)
                {
                    var org_val = convert_wave_org_data(wave_datas[j], i);
                    wave_data_buf.Add(org_val);
                    //wave_data_buf_inx += 1;
                }
            }
        }

        private void polyline_refresh(List<long> datas, int start_inx, int len)
        {
            // 原点相对坐标
            // y轴放大倍数
            // x轴放大倍数
            // x平移
            // y平移


            // x轴显示长度，像素
            // y轴显示长度， 像素
            double show_x_width = poly_canvas.ActualWidth - 20;
            double show_y_height = poly_canvas.ActualHeight;
            var d_len = datas.Count;
            if (start_inx < 0 || len <= 0 || (start_inx+len)>d_len)
            {
                DBG_GOBAL.WriteLine("input paramter error");
                return;
            }

            //DBG_GOBAL.WriteLine("ac width: " + show_x_width.ToString() + "ac heigth: " + show_y_height);

            double x_step = show_x_width / len;

            PointCollection pcs = new PointCollection();

            double x_pos = 0;

            long max_data = 0;
            long avg_data = 0;
            long min_data = 0;
            //foreach (var ival in datas)

            int end_inx = len + start_inx;

            for (var i=start_inx; i<end_inx; i++)
            {
                var ival = datas[i];
                max_data = max_data > ival ? max_data : ival;
                min_data = min_data < ival ? min_data : ival;
                avg_data += ival;
            }

            avg_data = avg_data / len;
            double avg_y = calc_point(avg_data);

            //var max_data = datas.Max();
            //var min_data = datas.Min();


            var data_width = max_data - min_data;
            //var data_width = 10000;

            //DBG_GOBAL.WriteLine("x_step: " + x_step.ToString() + "max_data:" + max_data.ToString() +
            //    "min_data:" + min_data.ToString() + "data_width:" + data_width.ToString() + 
            //    "avg_data:" + avg_data.ToString() + "==" + avg_y.ToString());

            pcs.Add(new Point(x_pos, 0));
            x_pos += x_step;
            for (var i = start_inx; i < end_inx; i++)
            {
                var point = new Point(x_pos,
                    calc_point(datas[i]) - avg_y + base_y + show_y_height/2);
                    //calc_point_y(datas[i], data_width, min_data) * show_y_height);
                pcs.Add(point);
                x_pos += x_step;
            }
            pcs.Add(new Point(x_pos, 0));
            //DBG_GOBAL.WriteLine("xxxx: " + x_pos.ToString());
            polyline.Points = pcs;
            //DBG_GOBAL.WriteLine("fresh ok");
        }

        private double calc_point_y(double ival, double data_width, double min_data)
        {
            return (ival-min_data) / data_width;
        }

        private bool hand_le = false;

        private int last_wave_len = 0;
        //private int last_wave_inx = 0;
        private int max_show_len = 1024;
        private bool hand_ctl = false;
        private int hand_start_inx = 0;
        private int hand_show_len = 0;

        private void cmd_refresh()
        {
            if (hand_ctl)
            {
                hand_show(hand_start_inx, hand_show_len);
            }
        }

        private void hand_show(int start_inx, int show_len)
        {
            var len = wave_data_buf.Count;
            if (show_len == -1)
            {
                show_len = len;
            }
            if (start_inx < 0)
            {
                start_inx = len + start_inx;
            }
            if (start_inx < 0 || start_inx >= len || start_inx+show_len > len) {
                DBG_GOBAL.WriteLine("error hand paramter");
                return;
            }
            hand_start_inx = start_inx;
            hand_show_len = show_len;
            polyline_refresh(wave_data_buf, start_inx, show_len);
        }

        private void timer_call()
        {
            int len;
            if (org_datas.Count > 0)
            {
                len = org_datas.Count;
                convert_wave_data_list(org_datas, len);
                // 也许会存在线程安全问题
                org_datas.RemoveRange(0, len);
            }
            // 获取坐标信息, 准备坐标

            len = wave_data_buf.Count;
            if (hand_ctl) return;
            if (len <= 0) return;
            if (len == last_wave_len)
            {
                if (!hand_le)
                    nutble.send_cmd("c_ok");
                //return;
            }
            last_wave_len = len;

            int show_len = max_show_len;
            int start_inx = len - max_show_len;

            if (len < max_show_len)
            {
                start_inx = 0;
                show_len = len;
            }
            
            polyline_refresh(wave_data_buf, start_inx, show_len);
        }


        private void timer_tick(object sender, object e)
        {
            timer_call();
            return;

            //DBG_GOBAL.WriteLine("xxxx");
            //var r = new Random(seed_i);


            //BitConverter.

            //pcs.Add(new Point(seed_i, r.Next() % 200 + 2));
            ////polyline.DataContext = l;
            //polyline.Points = pcs;
            //seed_i += 30;
            long cur_max_point = 0;
            long cur_min_point = 0;

            int tcount = org_datas.Count();
            if (org_datas.Count() > 0)
            {
                int show_count = 12;
                if(tcount > show_count)
                {
                    //show_count = tcount;
                    tcount = show_count;
                }


                //DBG_GOBAL.WriteLine("orgs_data.count: " + org_datas.Count());
                PointCollection pcs = new PointCollection();
                PointCollection last_pcs = polyline.Points;
                pcs.Add(new Point(0, 0));
                /* 8*60=480
                 * 
                 * 
                 */
                int inx = 0;
                var rlen = last_pcs.Count() + tcount * 40 - show_count * 40;
                //DBG_GOBAL.WriteLine("over count:" + rlen.ToString());
                if (rlen > 0)
                {
                    //for (var i = 1; i < rlen; i++)
                    for (var i = rlen; i < last_pcs.Count-1; i++)
                    {
                        //pcs.RemoveAt(i);
                        //pcs.Add(last_pcs[inx]);
                        pcs.Add(new Point(inx*x_mul, last_pcs[i].Y));
                        inx++;
                    }
                }
                else
                {
                    for (var i = 1; i < last_pcs.Count-1; i++)
                    {
                        //pcs.Add(last_pcs[i]);
                        pcs.Add(new Point(inx* x_mul, last_pcs[i].Y));
                        inx++;
                    }

                }
                //DBG_GOBAL.WriteLine("ddd inx: " + inx.ToString());

                for (var j =0; j< tcount; j++)
                {
                    var count = org_datas[j].Count();

                    //for align 2byte
                    if (count % 3 != 0)
                    {
                        count = count - count % 3;
                        //count--;
                    }

                    for (var i = 0; i < count; i+=3)
                    {
                        try
                        {
                            //var val = BitConverter.ToUInt16(org_datas[j], i);
                            //var val = org_datas[j][i];
                            var org_val = convert_wave_org_data(org_datas[j], i);

                            //var val = (long)(y_mul * 400 * 33 * org_val) / 0x7FFFFF;
                            double val = calc_point(org_val);
                            //if(i==0)
                            //{
                            //    DBG_GOBAL.WriteLine(org_val.ToString() + "****" + val.ToString());
                            //}
                            //if (val < 0)
                            //{
                            //    DBG_GOBAL.WriteLine("---" + y_mul.ToString());
                            //}
                                


                            //if (pcs_buf.Count() >= inx)
                            //{
                            //    pcs_buf.Add(new Point());
                            //}
                            //if (pcs.Count <= i)
                            //{
                            //pcs.Add(new Point(inx * 2, org_datas[j][i] * 4 - base_y));
                            pcs.Add(new Point(inx * x_mul, val - base_y));
                            inx++;
                            //pcs_buf[inx].X = inx * 2;
                            //pcs_buf[inx].Y = val * 2 - base_y;
                        }
                        catch
                        {
                            DBG_GOBAL.WriteLine("convert line error");
                        }
                        
                        //var p = pcs[i];
                        //p.X = i * 20;
                        //p.Y = org_datas[0][i];
                    }
                }
                pcs.Add(new Point(inx * x_mul, 0));
                polyline.Points = pcs;

                try
                {
                    org_datas.RemoveRange(0, tcount);
                }
                catch
                {
                    DBG_GOBAL.WriteLine("org_dats.removeRange:error");
                }
            }
            nutble.read_value();
        }

        private int convert_cmd_int(string[]val_str, int inx, int default_val=0)
        {
            try { 
                return int.Parse(val_str[inx]);
            }
            catch
            {
                return default_val;
            }
        }

        private void dispatch_cmd(String cmd)
        {
            var ck = cmd.Split(' ');
            DBG_GOBAL.WriteLine("debug# " + cmd);
            switch(ck[0])
            {
                case "sel":
                    try
                    {
                        if (ck.Length == 1)
                        {
                            DBG_GOBAL.WriteLine("usage: sel n, start from 0");
                        }else
                        {
                            int i = int.Parse(ck[1]);
                            nutble.sel_dev(i);
                        }
                    }
                    catch
                    {
                        DBG_GOBAL.WriteLine("exception cmd sel1");
                    }
                    break;
                case "sel_serv":
                    nutble.sel_service(0);
                    break;
                case "init_chars":
                    nutble.init_chars();
                    break;
                case "cmd":
                    {
                        if (ck[1] == "c_le")
                        {
                            hand_le = true;
                        }
                        if (ck[1] == "c_ok")
                        {
                            hand_ctl = false;
                            hand_le = false;
                        }
                        nutble.send_cmd(ck[1]);
                    }
                    break;
                case "pair":
                    nutble.pair_dev();
                    break;
                case "unpair":
                    nutble.unpair_dev();
                    break;
                case "conn":
                    nutble.conn_dev();
                    break;
                case "status":
                    nutble.status_dev();
                    break;
                case "serv_list":
                    nutble.list_service();
                    break;
                case "char_list":
                    nutble.list_service_characteristic();
                    break;
                case "basey":
                    base_y = convert_cmd_int(ck, 1, 0);
                    //try
                    //{

                    //    base_y = int.Parse(ck[1]);
                    //}
                    //catch
                    //{
                    //    //base_y = 0;
                    //}
                    break;
                case "zoom":
                    try
                    {
                        var zoom_x = convert_cmd_int(ck, 1, 1);
                        var zoom_y = convert_cmd_int(ck, 2, -1);
                        var p_x = convert_cmd_int(ck, 3, 0);
                        var p_y = convert_cmd_int(ck, 4, 475);
                        wave_zoom(zoom_x, zoom_y, p_x, p_y);
                    }
                    catch
                    {
                        DBG_GOBAL.WriteLine("error zoom");

                    }
                    break;
                case "show":
                    max_show_len = convert_cmd_int(ck, 1, max_show_len);
                    break;
                case "base":
                     x_mul = convert_cmd_int(ck, 1, 1);
                     y_mul = convert_cmd_int(ck, 2, 1);
                    break;
                case "hand":
                    int start_inx = convert_cmd_int(ck, 1, -max_show_len);
                    int show_len = convert_cmd_int(ck, 2, max_show_len);
                    hand_ctl = true;
                    hand_le = true;
                    nutble.send_cmd("c_le");
                    hand_show(start_inx, show_len);
                    break;
                default:
                    DBG_GOBAL.WriteLine("unkown command [" + ck.ToString() + "]");
                    DBG_GOBAL.WriteLine("usage: cmd [args]");
                    DBG_GOBAL.WriteLine("cmds: [sel|sel_serv|init_chars|conn|pair|unpair|status|cmd|serv_list|char_list|basey]");
                    break;
            }
            cmd_refresh();
        }

        private void scan_bt_Click(object sender, RoutedEventArgs e)
        {
            nutble.scan_dev();
        }

        private void stop_scan_bt_Click(object sender, RoutedEventArgs e)
        {
            nutble.stop_scan_dev();
        }

        private void cmd_textbox_TextChanged(object sender, TextChangedEventArgs e)
        {
            var cmd = cmd_textbox.Text;
            if (cmd.EndsWith("\r") || cmd.EndsWith("\n"))
            {
                cmd = cmd.Trim('\r').Trim('\n').Trim('\r').Trim(' ');
                // DBG_GOBAL.WriteLine(cmd);
                cmd_textbox.Text = "";
                dispatch_cmd(cmd);
            }
        }

        public void update_source(byte [] org_data)
        {
            //if (org_datas.Count() > 5) return;
            org_datas.Add(org_data);
            //lstSource = source;
            //t_source = source;
            //DataContext = nutble.lstSource;


            //DataContext = source;
            //await Dispatcher.TryRunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
            //{
            //    //var last_s = DataContext;
            //    DataContext = source;
            //    //last_s.d
            //}
            //);

            //Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync

            //Dispatcher.TryRunIdleAsync(t_update_data);

        }

        private void zoom_scan_bt_Copy_Click(object sender, RoutedEventArgs e)
        {
            wave_zoom(0, 0, 300, 200);
        }
    }
}
